package com.tuniu.agents.tools.flights.domestic.roundtrip;

import com.fasterxml.jackson.annotation.JsonClassDescription;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonPropertyDescription;
import com.tuniu.agents.advisor.ChatEnvironmentMemory;
import com.tuniu.agents.common.AgentsConstants;
import com.tuniu.agents.message.ToolLogger;
import com.tuniu.agents.message.ToolLoggerInfo;
import com.tuniu.agents.tools.flights.FlightProperties;
import com.tuniu.agents.tools.flights.enums.FlightToolEnum;
import com.tuniu.agents.tools.flights.model.DetailModel;
import com.tuniu.agents.tools.flights.model.Url;
import com.tuniu.agents.tools.flights.utils.CityCodeUtils;
import com.tuniu.agents.tools.flights.utils.FlightCommonUtils;
import com.tuniu.agents.tools.flights.utils.FlightMessageUtils;
import com.tuniu.agents.util.HttpManager;
import com.tuniu.agents.util.I18nUtil;
import com.tuniu.agents.util.JsonUtil;
import com.tuniu.agents.util.WebClientManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.ai.chat.model.ToolContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.BiFunction;

public class RoundFlightLowPriceQueryTool implements BiFunction<RoundFlightLowPriceQueryTool.Request, ToolContext, RoundFlightLowPriceQueryTool.Response>, ToolLogger {

    public static final String TOOLNAME = "RoundFlightLowPriceQueryTool";

    private static final Logger logger = LoggerFactory.getLogger(RoundFlightLowPriceQueryTool.class);

    private final String roundFlightLowPriceQueryUrl;

    @Autowired(required = false)
    private ChatEnvironmentMemory chatEnvironmentMemory;

    @Autowired
    private HttpManager httpManager;

    public RoundFlightLowPriceQueryTool(FlightProperties properties) {
        this.roundFlightLowPriceQueryUrl = properties.getRoundFlightLowPriceQueryToolUrl();
    }

    @ToolLoggerInfo(beanName = TOOLNAME)
    @Override
    public Response apply(Request request, ToolContext context) {
        try {
            // Get the custom parameters corresponding to the session
            logger.info("RoundFlightLowPriceQueryTool request parameters: {} flightApiUrl: {}", request, roundFlightLowPriceQueryUrl);
            Map<String, Object> map = context.getContext();

            // Prevent malicious attacks by not calling the interface on the mini program side
            String clientType = String.valueOf(chatEnvironmentMemory.get((String) map.get("conversation_id"), AgentsConstants.AgentsConversationEnvironmentKey.CLIENT_TYPE));
            if (AgentsConstants.FrontendParamKey.WECHAT_CLIENT_TYPE.contains(clientType)) {
                return new Response(false, null, null, 0);
            }

            Response post = httpManager.post(roundFlightLowPriceQueryUrl, request, Response.class);
            if (post.queryId != null) {
                Map<String, Object> hashMap = chatEnvironmentMemory.get((String) map.get("conversation_id"));
                hashMap.put("queryId", post.queryId);
                hashMap.put("departureCityName", request.departureCityName);
                hashMap.put("arrivalCityName", request.arrivalCityName);
                hashMap.put("departureDate", request.departureDate);
                hashMap.put("backDepartureDate", request.backDepartureDate);
                chatEnvironmentMemory.add((String) map.get("conversation_id"), hashMap);
            }
            if (CollectionUtils.isEmpty(post.data)) {
                return post;
            }
            return post.buildResponse();
        } catch (Exception e) {
            logger.error("RoundFlightLowPriceQueryTool request exception: {}", e.getMessage());
            return null;
        }
    }

    @Override
    public List<String> before(Object request, ToolContext context) {
        String conversationId = (String) context.getContext().get("conversation_id");
        Request rRequest = (Request) request;
        if(StringUtils.hasText(rRequest.departureCityName) && StringUtils.hasText(rRequest.arrivalCityName) && StringUtils.hasText(rRequest.departureDate) && StringUtils.hasText(rRequest.backDepartureDate)) {
            return List.of(FlightMessageUtils.getShowMessage(rRequest.departureCityName, rRequest.arrivalCityName, rRequest.departureDate, rRequest.backDepartureDate,conversationId),
                    I18nUtil.getMessage(conversationId, FlightToolEnum.ROUND_FLIGHT_LOW_PRICE_LIST.getBefore()));
        }
        return null;
    }

    @Override
    public List<String> after(Object request, ToolContext context, Object response) {
        Response result = (Response) response;
        String conversationId = (String) context.getContext().get("conversation_id");
        if(response != null && result.data() != null) {
            return List.of(FlightMessageUtils.getToolMessage(result.data().size(),conversationId));
        }
        return null;
    }

    @JsonClassDescription("国内机票往返列表查询入参")
    public record Request(
            @JsonProperty(required = true,
                    value = "departureCityName") @JsonPropertyDescription("出发城市，出发城市和到达城市不可为同一个") String departureCityName,
            @JsonProperty(required = true,
                    value = "arrivalCityName") @JsonPropertyDescription("到达城市，出发城市和到达城市不可为同一个") String arrivalCityName,
            @JsonProperty(required = true,
                    value = "departureDate") @JsonPropertyDescription("出发日期,格式:yyyy-MM-dd") String departureDate,
            @JsonProperty(required = true,
                    value = "backDepartureDate") @JsonPropertyDescription("返回日期,格式:yyyy-MM-dd") String backDepartureDate) {
    }

    @JsonClassDescription("国内机票往返列表查询返参")
    public record Response(
            @JsonProperty(required = true,
                    value = "successCode") @JsonPropertyDescription("成功标识") boolean successCode,
            @JsonProperty(required = true,
                    value = "queryId") @JsonPropertyDescription("查询ID") String queryId,
            @JsonProperty(required = true,
                    value = "data") @JsonPropertyDescription("返回的航班数据") List<FlightInfo> data,
            @JsonProperty(required = true,
                    value = "totalPageNum") @JsonPropertyDescription("分页数") int totalPageNum) {

        private String convertTimeFormat(String time) {
            if (StringUtils.hasText(time)) {
                return time.replace("m", "分").replace("h", "时");
            }
            return time;
        }

        public Response buildResponse() {
            List<FlightInfo> flightInfos = data().stream().map(flightInfo -> {
                List<FlightItem> flightItems = flightInfo.flightInfo.stream().map(flightItem -> {
                    return new FlightItem(
                            flightItem.airlineCompany,
                            flightItem.arrivalAirport,
                            flightItem.arrivalTerminal,
                            flightItem.arrivalTime,
                            flightItem.cabinClass,
                            flightItem.craftType,
                            flightItem.departureAirport,
                            flightItem.departureTerminal,
                            flightItem.departureTime,
                            flightItem.flightNumber,
                            convertTimeFormat(flightItem.flyTime),
                            flightItem.planModel,
                            flightItem.remainingSeats,
                            flightItem.type,
                            convertTimeFormat(flightItem.totalDuration),
                            flightItem.stopPoint,
                            flightItem.transferPoint,
                            convertTimeFormat(flightItem.stopTime),
                            convertTimeFormat(flightItem.transferTime),
                            flightItem.airComImageUrl,
                            flightItem.departureCityName,
                            flightItem.arrivalCityName
                    );
                }).toList();
                return new FlightInfo(
                        flightInfo.priceInfo(),
                        flightItems,
                        flightInfo.buildFlightKey(),
                        flightInfo.priceInfo(),
                        flightInfo.url()
                );
            }).toList();

            return new Response(
                    this.successCode,
                    this.queryId,
                    flightInfos,
                    this.totalPageNum
            );
        }
    }

    @JsonClassDescription("国内航班数据")
    public record FlightInfo(
            @JsonProperty(value = "priceInfo") @JsonPropertyDescription("往返总价格") String priceInfo,
            @JsonProperty(required = true, value = "flightInfo") @JsonPropertyDescription("往返航班，0 去程，1 返程") List<FlightItem> flightInfo,
            @JsonProperty(value = "flightKey") @JsonPropertyDescription("航班唯一标识，格式为：去程航班号#返程航班号") String flightKey,
            @JsonProperty(value = "priceWithTax") @JsonPropertyDescription("成人价格") String priceWithTax,
            @JsonProperty(value = "url") @JsonPropertyDescription("收藏链接") Url url) {

        public String buildFlightKey() {
            return flightInfo().get(0).flightNumber() + "#" + flightInfo().get(1).flightNumber();
        }
    }

    @JsonClassDescription("国内航班列表")
    public record FlightItem(
            @JsonProperty(value = "airlineCompany") @JsonPropertyDescription("航司") String airlineCompany,
            @JsonProperty(value = "arrivalAirport") @JsonPropertyDescription("到达机场") String arrivalAirport,
            @JsonProperty(value = "arrivalTerminal") @JsonPropertyDescription("到达航站楼") String arrivalTerminal,
            @JsonProperty(required = true, value = "arrivalTime") @JsonPropertyDescription("到达时间") String arrivalTime,
            @JsonProperty(value = "cabinClass") @JsonPropertyDescription("舱等") String cabinClass,
            @JsonProperty(value = "craftType") @JsonPropertyDescription("机型") String craftType,
            @JsonProperty(value = "departureAirport") @JsonPropertyDescription("出发机场") String departureAirport,
            @JsonProperty(value = "departureTerminal") @JsonPropertyDescription("出发航站楼") String departureTerminal,
            @JsonProperty(required = true, value = "departureTime") @JsonPropertyDescription("出发时间") String departureTime,
            @JsonProperty(value = "flightNumber") @JsonPropertyDescription("航班号") String flightNumber,
            @JsonProperty(value = "flyTime") @JsonPropertyDescription("飞行时长") String flyTime,
            @JsonProperty(value = "planModel") @JsonPropertyDescription("机型大小") String planModel,
            @JsonProperty(value = "remainingSeats") @JsonPropertyDescription("剩余位置") String remainingSeats,
            @JsonProperty(value = "type") @JsonPropertyDescription("航班类型：直飞，中转，联程，经停") String type,
            @JsonProperty(value = "totalDuration") @JsonPropertyDescription("总时长") String totalDuration,
            @JsonProperty(value = "stopPoint") @JsonPropertyDescription("经停城市") String stopPoint,
            @JsonProperty(value = "transferPoint") @JsonPropertyDescription("中转城市") String transferPoint,
            @JsonProperty(value = "stopTime") @JsonPropertyDescription("经停时长") String stopTime,
            @JsonProperty(value = "transferTime") @JsonPropertyDescription("中转时长") String transferTime,
            @JsonProperty(value = "airComImageUrl") @JsonPropertyDescription("航司图标链接") List<String> airComImageUrl,

            @JsonProperty(value = "departureCityName") @JsonPropertyDescription("出发城市") String departureCityName,

            @JsonProperty(value = "arrivalCityName") @JsonPropertyDescription("到达城市") String arrivalCityName) {

        public FlightItem buildFlightItem(String departureCityName, String arrivalCityName) {
            return new FlightItem(
                    this.airlineCompany,
                    this.arrivalAirport,
                    this.arrivalTerminal,
                    this.arrivalTime,
                    this.cabinClass,
                    this.craftType,
                    this.departureAirport,
                    this.departureTerminal,
                    this.departureTime,
                    this.flightNumber,
                    this.flyTime,
                    this.planModel,
                    this.remainingSeats,
                    this.type,
                    this.totalDuration,
                    this.stopPoint,
                    this.transferPoint,
                    this.stopTime,
                    this.transferTime,
                    this.airComImageUrl,
                    departureCityName,
                    arrivalCityName
            );
        }
    }

    public static Map<String, Object> buildCard(String flightsJson, Map<String,Object> hashMap) {
        Map<String, Object> map = new HashMap<>();
        if (flightsJson == null || flightsJson.isEmpty()) {
            return null;
        }

        Response response = JsonUtil.fromJson(flightsJson, RoundFlightLowPriceQueryTool.Response.class);
        if (response == null) {
            return null;
        }
        HttpManager httpManager = new WebClientManager();
        //CityCodeUtils cityCodeUtils = new CityCodeUtils();
        CityCodeUtils.Ext departureCityExt = CityCodeUtils.getCityCode(httpManager, (String)hashMap.get("departureCityName"));
        CityCodeUtils.Ext arrivalCityExt = CityCodeUtils.getCityCode(httpManager, (String)hashMap.get("arrivalCityName"));
        for(FlightInfo flightInfo : response.data){
            for (int i = 0; i < flightInfo.flightInfo.size(); i++) {
                FlightItem flightItem = flightInfo.flightInfo.get(i);
                FlightItem updatedFlightItem = null;
                if(i==0) {
                    updatedFlightItem = flightItem.buildFlightItem((String) hashMap.get("departureCityName"), (String) hashMap.get("arrivalCityName"));
                }else {
                    updatedFlightItem = flightItem.buildFlightItem((String) hashMap.get("arrivalCityName"), (String) hashMap.get("departureCityName"));
                }
                flightInfo.flightInfo.set(i, updatedFlightItem);
            }

            Url url = new Url();
            url.setAppUrl(buildUrl(flightInfo, (String)hashMap.get("queryId"), hashMap, departureCityExt, arrivalCityExt));

            map.put(flightInfo.flightKey, new FlightInfo(
                    flightInfo.priceInfo(),
                    flightInfo.flightInfo(),
                    flightInfo.flightKey(),
                    flightInfo.priceWithTax(),
                    url
            ));
        }
        return map;
    }

    private static String buildUrl(FlightInfo flightInfo,
                                   String queryId,
                                   Map<String, Object> qParams,
                                   CityCodeUtils.Ext departureCityExt,
                                   CityCodeUtils.Ext arrivalCityExt) {
        // 国内跳转链接
        try {
            DetailModel model = new DetailModel();
            model.setQueryId(queryId);
            model.setFlightNos(flightInfo.flightKey());
            model.setDeptDate((String)qParams.get("departureDate"));
            model.setBackDate((String)qParams.get("backDepartureDate"));
//            model.convertAndSetCabinClass(item.cabinClass());
            model.setTransfer(false);
            model.setOrgCityName((String)qParams.get("departureCityName"));
            model.setOrgCityCode(departureCityExt.getCode());
            model.setOrgCityIataCode(departureCityExt.getIata_code());
            model.setDstCityName((String)qParams.get("arrivalCityName"));
            model.setDstCityCode(arrivalCityExt.getCode());
            model.setDstCityIataCode(arrivalCityExt.getIata_code());
            model.setSystemId(FlightCommonUtils.DOMESTIC_FLIGHT_SYSTEM_ID);
            String json = JsonUtil.toJson(model);
            return "tuniuapp://page?iosPageName=TNAirplaneDetailV2ViewController" +
                    "&androidPageName=com.tuniu.plane.activity.PlaneDomesticDetailActivity&pluginType=3&parameters="
                    + URLEncoder.encode(json, StandardCharsets.UTF_8);

        } catch (Exception e) {
            logger.error("buildUrl 异常：{} ", e.getMessage(), e);
            return null;
        }

    }

    public static String buildMarkdown(String toolResponse) {
        Response response = JsonUtil.fromJson(toolResponse, Response.class);

        StringBuilder markdown = new StringBuilder();
        markdown.append("| Flight Key | 成人价格 ");
        markdown.append("| 去程航班号 | 去程出发时间 | 去程到达时间 | 去程出发机场 | 去程到达机场 | 去程剩余位置 | 去程总时长 | 去程舱等 | 去程航班类型 | 去程中转城市 | 去程中转时长 ");
        markdown.append("| 返程航班号 | 返程出发时间 | 返程到达时间 | 返程出发机场 | 返程到达机场 | 返程剩余位置 | 返程总时长 | 返程舱等 | 返程航班类型 | 返程中转城市 | 返程中转时长 |\n");
        markdown.append("| --- | --- ");
        markdown.append("| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | ---  ");
        markdown.append("| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |\n");

        for (FlightInfo flightInfo : response.data()) {
            FlightItem goFlightItem = flightInfo.flightInfo().get(0);
            FlightItem backFlightItem = flightInfo.flightInfo().get(1);

            markdown.append("|").append(flightInfo.flightKey())
                    .append("|").append(flightInfo.priceWithTax());

            // Outbound flight information
            buildFlightItem(goFlightItem, markdown);

            // Return flight information
            buildFlightItem(backFlightItem, markdown);

            markdown.append("|\n");
        }
        markdown.append("\n");

        return markdown.toString();
    }

    public static void buildFlightItem(FlightItem flightItem, StringBuilder markdown) {
        // Concatenate airport and terminal
        String goDepartureAirport = flightItem.departureAirport();
        if (StringUtils.hasText(flightItem.departureTerminal())) {
            goDepartureAirport += flightItem.departureTerminal();
        }
        String goArrivalAirport = flightItem.arrivalAirport();
        if (StringUtils.hasText(flightItem.arrivalTerminal())) {
            goArrivalAirport += flightItem.arrivalTerminal();
        }

        // Get transfer or stop cities and times
        String transferCity = "";
        String transferTime = "";
        if ("中转".equals(flightItem.type())) {
            transferCity = flightItem.transferPoint();
            transferTime = flightItem.transferTime();
        } else if ("经停".equals(flightItem.type())) {
            transferCity = flightItem.stopPoint();
            transferTime = flightItem.stopTime();
        }

        markdown.append("|").append(flightItem.flightNumber())
                .append("|").append(flightItem.departureTime())
                .append("|").append(flightItem.arrivalTime())
                .append("|").append(goDepartureAirport)
                .append("|").append(goArrivalAirport)
                .append("|").append(flightItem.remainingSeats())
                .append("|").append(flightItem.totalDuration())
                .append("|").append(flightItem.cabinClass())
                .append("|").append(flightItem.type())
                .append("|").append(transferCity)
                .append("|").append(transferTime);
    }
}